/*
 Автор: Швейкин Евгений Юрьевич

 Функциональное описание:
    Интересно, а как задается начало кадра, каким сигналом выполняется переход на первую строку?
Может это сигнал vrdata? Тогда его нужно подавать только в конце кадра, а управление экспозицией
будет выполняться только сигналом vldata.
    
    Макимальное время экспозиции имеет ограниченное значение. 
Если    ширина  320,
        высота  240,
        частота видео 50Гц,
        частота считывания из сдвигового регистра 10 МГц, 
то на работу с 1 строкой максимум 1/50/240 - 320 * 0,1= 83,3 - 32 = 61,3 мкс. 
Это без учета дополнительных сигналов (сброса, выборки и т.д.), не маловато ли?
        
*/
`timescale 1ns / 10ps
//    ********************** НАЧАЛО МОДУЛЯ *********************************************************
module mod_matrix_ctrl
    #(  parameter freq_p            = 100_000_000,
        parameter col_p             = 320,
        parameter row_p             = 240,
        parameter vldata_up_ns_p    = 1,    // Длительность vldata в 1 (для зарядя ячеек)
        parameter exp_ns_p          = 1,    // Время экспозиции
        parameter vhdata_ns_p       = 1,    // Длительность 1 vhdata
        parameter sample_ns_p       = 1,    // длительность sample_ns_p
        parameter sync_ns_p         = 1     // период синхрочастот
    )
    (   input  logic            reset_n,
        input  logic            clk,

        input  logic            start_i,
        
        output logic            vldata_o,   // Управление левым вертикальным регистром
                                            // 1 - заряд ячеек до 1, 0 - разряд фототоком
                                            // Определяет время экспозиции
        output logic            vrdata_o,   // Управление правым вертикальным регистром
                                            // Сброс фотоячеек

        output logic            vhdata_o,   // Подключим к нагрузке
        
        output logic            vlsync_o,   // Синхроимпульсы
        output logic            vrsync_o,
        output logic            vhsync_o,
        
        output logic            vsample_o,  // Защелкнуть ячейки в сдвиговом регистре
        output logic            vclamp_o,   // Очистить выходной буфер
        
        output logic [9:0]      row,
        output logic [9:0]      col
        
        
    );

//    ********************* КОНСТАНТЫ **************************************************************
    localparam longint vldata_up_lp     = vldata_up_ns_p* freq_p / 1_000_000_000;
    localparam longint exp_lp           = exp_ns_p      * freq_p / 1_000_000_000;
    localparam longint sample_lp        = sample_ns_p   * freq_p / 1_000_000_000;
    localparam longint vhdata_lp        = vhdata_ns_p   * freq_p / 1_000_000_000 - sample_lp;
    localparam longint vhsync_lp        = sync_ns_p     * freq_p / 1_000_000_000;

//    ********************* СОЗДАНИЕ И ОПИСАНИЕ ПЕРЕМЕННЫХ *****************************************
    
    enum logic [7:0]    {   IDLE_s          = 8'b0000_0001,
                            ROW_FILL_s      = 8'b0000_0010,
                            ROW_EXP_s       = 8'b0000_0100,
                            ROW_CONNECT_s   = 8'b0000_1000,
                            ROW_SAMPLE_s    = 8'b0001_0000,
                            COL_READ_s      = 8'b0010_0000,
                            COL_CTRL_s      = 8'b0100_0000,
                            ROW_CTRL_s      = 8'b1000_0000
                        } state_matrix_ctrl, next_state;

    logic [9:0]     row_cnt;
    logic [9:0]     col_cnt;
    
    logic [31:0]    time_cnt;
    logic           time_cnt_rst;
    logic           time_cnt_ena;
    
    logic           start_0;
    logic           start_1;
    logic           start_rise;
    
    logic           vclamp_0;
    logic           vclamp_1;
    logic           vclamp_fall;
    
    logic           vrdata_0;
    logic           vrdata_1;
    logic           vrdata_rise;
    
    logic           vlsync;
    logic           vrsync;
    logic           vhsync;    
    
    logic           vlsync_ena;
    logic           vrsync_ena;
    logic           vhsync_ena; 
    
    
//  ********************* БЛОК НЕПРЕРЫВНЫХ НАЗНАЧЕНИЙ ASSIGN ***************************************
    assign vlsync_o  = vlsync;//vlsync_ena  & vlsync;
    assign vrsync_o  = vrsync;//vrsync_ena  & vrsync;
    // assign vhsync_o  = vhsync_ena  & vhsync;
    
    assign row  =   row_cnt;
    assign col  =   col_cnt;

//  ********************* ПОДКЛЮЧЕНИЕ МОДУЛЕЙ ******************************************************
    mod_impulse_former
    #(  .freq_p(freq_p),
        .up_ns_p(sync_ns_p/2),
        .period_ns_p(sync_ns_p),
        .phase_p(0)
    )
    gen_vlsync
    (   .reset_n(~start_rise),
        .clk(clk),
        .impulse(vlsync)
    );
    
    mod_impulse_former
    #(  .freq_p(freq_p),
        .up_ns_p(sync_ns_p/2),
        .period_ns_p(sync_ns_p),
        .phase_p(0)
    )
    gen_vrsync
    (   .reset_n(~start_rise),
        .clk(clk),
        .impulse(vrsync)
    );
    
    mod_impulse_former
    #(  .freq_p(freq_p),
        .up_ns_p(sync_ns_p/2),
        .period_ns_p(sync_ns_p),
        .phase_p(0)
    )
    gen_vhsync
    (   .reset_n(~(time_cnt_rst)),
        .clk(clk),
        .impulse(vhsync)
    );
    
//  ********************* ОПИСАНИЕ ПРОЦЕССОВ *******************************************************

    always_ff @(posedge clk)
    begin
        start_0    <= start_i;
        start_1    <= start_0;
        start_rise <= start_0 & (~start_1);
        
        vclamp_0 <= vclamp_o;
        vclamp_1 <= vclamp_0;
        vclamp_fall <= vclamp_1 & (~vclamp_0);
        
        vrdata_0 <= vrdata_o;
        vrdata_1 <= vrdata_0;
        vrdata_rise <= vrdata_0 & (~vrdata_1);
    end
    
    always_ff @(posedge clk)
    begin
        if (vldata_o)
            col_cnt <= 10'd0;
        else if (vclamp_fall)
            col_cnt <= col_cnt + 10'd1;
        else;
        
        if (state_matrix_ctrl == IDLE_s)
            row_cnt <= 10'd0;
        else if (state_matrix_ctrl == ROW_CTRL_s)
            row_cnt <= row_cnt + 10'd1;
        else;
        
    end
        
    // Переключение состояний автомата (классическое описание)
    always_ff @(posedge clk,  negedge reset_n)
    begin
        if (!reset_n)
            state_matrix_ctrl    <= IDLE_s;
        else
            state_matrix_ctrl    <= next_state; 
    end

    // Определение следующего состояния автомата
    always_comb        
    begin    
        next_state    = state_matrix_ctrl;
        unique case (state_matrix_ctrl)
            IDLE_s:                 
            begin
                if (start_rise) 
                    next_state  = ROW_FILL_s; 
                else;
            end
            
            ROW_FILL_s:              // Зарядка фоточувствительных ячеек
            begin
                if (time_cnt_rst) begin
                    next_state = ROW_EXP_s;
                end
                else;
            end
            
            ROW_EXP_s:              // Накопление сигнала за время экспозиции. Разрядка
            begin                   // фототоком
                if (time_cnt_rst) begin
                    next_state = ROW_CONNECT_s;
                end
                else;
            end
            
            ROW_CONNECT_s:          // Подключение диодов столбцовым нагрузочным элементам 
            begin                   // и входам схемы выборки-хранения мультиплексора
                if (time_cnt_rst) begin
                    next_state = ROW_SAMPLE_s;
                end
                else;
            end
            
            ROW_SAMPLE_s:           // Управление схемой выборки-хранения мультиплексора
                                    // Думаю это значит, что значения ячеек строки защелкиваются
            begin                   // в сдвиговом регистре
                if (time_cnt_rst) begin
                    next_state = COL_READ_s;
                end
                else;
            end
            
            COL_READ_s:             // Последовательное подключение элементов сдвигового 
                                    // регистра к выходному повторителю.
            begin
                if (col_cnt >= col_p)
                    next_state = ROW_CTRL_s;
                else;
            end
            
            ROW_CTRL_s:
            begin                   // и входам схемы выборки-хранения мультиплексора
                if (row_cnt >= row_p - 1)
                    next_state = IDLE_s;
                else
                    next_state = ROW_FILL_s;
            end
        endcase
    end    

    // Описание выходов автомата 
    always_comb
    begin
        vlsync_ena  = 1'b0;
        vrsync_ena  = 1'b0;
        vhsync_ena  = 1'b0;
        
        vhsync_o    = 1'b0;

        vldata_o      = 1'b0;
        vrdata_o      = 1'b0;
        vhdata_o      = 1'b0;
        
        vsample_o     = 1'b0;
        vclamp_o      = 1'b0;
        unique case (state_matrix_ctrl)
            IDLE_s:
            begin
                vrdata_o    = 1'b1;     // Ресетнули ячейки строки
                vrsync_ena  = 1'b1;
            end
            
            ROW_FILL_s:
            begin
                vldata_o    = 1'b1;     // Зарядили ячейки
                vlsync_ena  = 1'b1;
            end
                
            ROW_EXP_s:
            begin
                vldata_o    = 1'b0;     // разряжаем ячейки
                vlsync_ena  = 1'b1;
            end
            
            ROW_CONNECT_s:
            begin
                vhdata_o  = 1'b1;        // Подключим к нагрузке
            end
            
            ROW_SAMPLE_s:
            begin
                vhdata_o    = 1'b1;     // Подключим к нагрузке
                vsample_o   = 1'b1;     // Защелкнем в сдвиговый регистр
            end
            
            COL_READ_s:
            begin
                vhsync_o    = vhsync;   // Смещаем элементы сдвигового регистра
                vclamp_o    = vhsync;   // Очистили выходной буфер
            end
        endcase
    end

    initial time_cnt =0;
    
    always_ff @(posedge clk)
    begin
        if (time_cnt_rst)
            time_cnt <= 32'd0;
        else if (time_cnt_ena)
            time_cnt <= time_cnt + 32'd1;
        else;
    end

    // Установка сигналов управления счетчиком времени
    always_comb
    begin
        time_cnt_rst    <= 1'b0;
        time_cnt_ena    <= 1'b1;
        unique case (state_matrix_ctrl)
            IDLE_s:
            begin
                time_cnt_rst    <= 1'b1;
            end
            
            ROW_FILL_s:
            begin
                if (time_cnt >= vldata_up_lp - 1)
                    time_cnt_rst    <= 1'b1;
                else;            
            end
            
            ROW_EXP_s:
            begin
                if (time_cnt >= exp_lp - 1)
                    time_cnt_rst    <= 1'b1;
                else;
            end
            
            ROW_CONNECT_s:
            begin
                if (time_cnt >= vhdata_lp - 1)
                    time_cnt_rst    <= 1'b1;
                else;
            end
            
            ROW_SAMPLE_s:
            begin
                if (time_cnt >= sample_lp - 1)
                    time_cnt_rst    <= 1'b1;
                else;
            end
            
            COL_READ_s:
            begin
                time_cnt_ena    <= 1'b0;
            end

            COL_CTRL_s:
            begin
                time_cnt_ena    <= 1'b0;
            end
            
            ROW_CTRL_s:
            begin
                time_cnt_ena    <= 1'b0;
            end
        endcase
    end

endmodule